package org.happy.tester.agent;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import org.apache.commons.lang3.StringUtils;
import org.happy.tester.common.model.PointcutDO;
import org.happy.tester.common.http.req.PointcutRequest;

import java.lang.instrument.Instrumentation;
import java.lang.instrument.UnmodifiableClassException;
import java.util.*;
import java.util.stream.Collectors;

public class AgentMain {

    public static void agentmain(String agentArgs, Instrumentation instrumentation) throws UnmodifiableClassException {
        System.out.println("load agent success");
        if (instrumentation.isRetransformClassesSupported()) {
            PointcutRequest pointcutRequest = JSON.parseObject(agentArgs, PointcutRequest.class);
            List<PointcutDO> pointcutDOS = pointcutRequest.getPointcutDOS();
            List<Class<?>> loadedClasses = Arrays.asList(instrumentation.getAllLoadedClasses());
            Set<String> bothOfClassesNames = getBothOfSomeClassesNames(pointcutDOS, loadedClasses);
            System.out.println(JSONObject.toJSONString(bothOfClassesNames));
            List<Class<?>> toBeRetransformClasses = Optional
                    .ofNullable(loadedClasses)
                    .orElse(new ArrayList<>())
                    .stream()
                    .filter(loadedClass -> bothOfClassesNames.contains(loadedClass.getName()))
                    .collect(Collectors.toList());
            //解决方法重载,key:className,value:PointcutDO
            Map<String, List<PointcutDO>> pointcutDOMap = Optional
                    .ofNullable(pointcutDOS)
                    .orElse(new ArrayList<>())
                    .stream()
                    .filter(pointcutDO -> bothOfClassesNames.contains(pointcutDO.getClassName()))
                    .collect(Collectors.toMap(PointcutDO::getClassName
                            , pointcutDO -> Arrays.asList(pointcutDO)
                            , (List<PointcutDO> newPointcutDO, List<PointcutDO> oldPointcutDO) -> newPointcutDO));
            TargetClassTransFormer transFormer = new TargetClassTransFormer(pointcutRequest, pointcutDOMap);
            try {
                instrumentation.addTransformer(transFormer, true);
                instrumentation.retransformClasses(toBeRetransformClasses.toArray(new Class[toBeRetransformClasses.size()]));
            } finally {
                instrumentation.removeTransformer(transFormer);
            }
        }
    }

    private static Set<String> getBothOfSomeClassesNames(List<PointcutDO> pointcutDOS, List<Class<?>> classes) {
        Set<String> pointcutClassesNames = Optional.ofNullable(pointcutDOS)
                .orElse(new ArrayList<>())
                .stream()
                .map(PointcutDO::getClassName)
                .filter(className -> StringUtils.isNotEmpty(className))
                .collect(Collectors.toSet());
        Set<String> bothOfClassesNames = Optional
                .ofNullable(classes)
                .orElse(new ArrayList<>())
                .stream()
                .map(Class::getName)
                .filter(className -> pointcutClassesNames.contains(className))
                .collect(Collectors.toSet());
        return bothOfClassesNames;
    }
}